package stream.method;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

/**
 * @CreateBy zhangdingyi
 * @Date 2022-10-12
 * @Content 群组和分区、下游收集器
 */
public class Seven {
    public static void main(String[] args) {
        //testLocal();

        testColleGB();
    }

    /**
     * 简单的说 Collectors.groupingBy 就是 以某值为key 把 这个key相同的值 作为一类 整合成一个集合
     * Collectors.groupingBy 的基本使用
     * 这个呢 就是 map<String, list<City>> 简而言之就是 广东 = {广州，中山}
     * 把 相同属性的集合混合成一个
     *
     * <p>
     * 以某值为参照 合计 这个相同参照的某值
     * 然后就是 下游收集器 主要以 groupingBy 为主方法
     *
     * 次方法调用 -summingInt （L I D） 合计数字 会接受一个函数作为引元，将该函数应用到下游元素中，并产生它们的和
     * groupingBy(City::getState, summingInt(City::getPopulation))
     * 这个就是合计 getState 一样的 合计 getPopulation -但是这个仅有 基本类型三兄弟（L I D）
     *
     * <p>
     * 这里使用 新值 保存合适的 旧值
     * 次方法调用 -reducing 整合字符串
     * groupingBy(City::getState, reducing("", City::getName,
     * (x, j) -> x.length() == 0 ? j : x + "," + j))
     * 以 getState 为 key 然后 组合 getName 的值 整合成 map<String, String> 的集合
     *
     *
     * <p>
     * 简单的说 这个就是 以设置的值为参照 计算该值出现的次数
     * counting（）产生一个可以对收集到的元素进行计数的收集器。
     * 次方法调用 -counting 会产生收集到的元素的个数
     * 把 getState 相同的进行合计并计算出数字表示出现了多少次
     * groupingBy(City::getState, counting())
     *
     * <p>
     * 简单的说这个就是取最大最小值
     * -maxBy/minBy 产生一个收集器，使用comparator指定的排序方法，计算收集到的元素中的最大值和最小值。
     * 次方法调用 -maxBy/minBy 以某值为key 取其中的最大值/最小值（仅能是数字,不是数字也行），这个还需要 添加 Comparator.comparing 保证正常输出
     * groupingBy(City::getState, maxBy(Comparator.comparing(City::getPopulation)))
     *
     * <p>
     * 简单的说就是 mapping 可以把值传递给 maxBy 方法，然后这个方法就可以获取其的长度（原先直接放字符串会报错）
     * 产生一个收集器，它会产生一个映射表，其键是将mapper应用到收集到的数据上而产生的，其值是使用downstream收集器收集到的具有相同键的元素。
     * 次方法调用 -mapping方法会产生将函数应用到下游结果上的收集器，并将函数值传递给另一个收集器（还可以整合成 集合）
     * groupingBy(City::getState, mapping(City::getName, maxBy(Comparator.comparing(String::length))))
     *
     * <p>
     * 这个就是 把基本类型 是 数字的 整合起来并给予对应的整理的结果值
     * 次方法调用 -summarizingInt（L I D）
     */
    static void testColleGB() {
        Stream<City> cityStream = readCities();

        Map<String, List<City>> collectCity = cityStream.collect(
                Collectors.groupingBy(City::getState)
        );
        //{直辖市=[City{name='上海', state='直辖市', population=4}], 首都=[City{name='北京', state='首都', population=3}], 广东=[City{name='广州', state='广东', population=6}, City{name='中山', state='广东', population=11}]}
        System.out.println("collectCity -> " + collectCity);

        //下面两个都是 下游收集器 -静态直接调用
        //这个就是 整合某个 字段，汇总出数字的合计 //以某值为key 合计某值
        Map<String, Integer> collect = readCities().collect(
                groupingBy(City::getState, summingInt(City::getPopulation))
        );
        //{直辖市=4, 首都=3, 广东=17, 广西=42}
        System.out.println("collect -> " + collect);

        // 以 getState 为 key，整合 相同的 字符串
        Map<String, String> collect1 = readCities().collect(
                groupingBy(
                        City::getState, reducing("", City::getName,
                                (s, t) -> {
                                    System.out.println("集合遍历开始");
                                    System.out.println("第一个值（新集合的值） s -> " + s);
                                    System.out.println("第二个值（原来集合的值） t -> " + t);
                                    System.out.println("集合遍历结束");
                                    return s.length() == 0 ? t : s + "," + t;
                                }))
        );
        // {直辖市=上海, 首都=北京, 广东=广州,中山, 广西=柳州,南宁,桂林,来宾,贵港,百色,崇左}
        System.out.println("collect1 -> " + collect1);

        //记数 就是把相同的 内容进行 合计
        Map<String, Long> collectCounting = readCities().collect(groupingBy(City::getName, counting()));
        //collectCounting -> {南宁=1, 崇左=1, 广州=1, 上海=1, 来宾=1, 柳州=1, 桂林=1, 百色=1, 贵港=1, 中山=1, 北京=1}
        System.out.println("collectCounting -> " + collectCounting);

        Map<String, Long> collectCounting2 = readCities().collect(groupingBy(City::getState, counting()));
        //collectCounting2 -> {直辖市=1, 首都=1, 广东=2, 广西=7}
        System.out.println("collectCounting2 -> " + collectCounting2);

        //取最值 最大值 minBy 是最小值
        Map<String, Optional<City>> collectMaxBy = readCities().collect(groupingBy(City::getState, maxBy(Comparator.comparing(City::getPopulation))));
        //{直辖市=Optional[City{name='上海', state='直辖市', population=4}],
        // 首都=Optional[City{name='北京', state='首都', population=3}],
        // 广东=Optional[City{name='中山', state='广东', population=11}],
        // 广西=Optional[City{name='崇左', state='广西', population=10}]}
        System.out.println("collectMaxBy -> " + collectMaxBy);

        //mapping方法会产生将函数应用到下游结果上的收集器，并将函数值传递给另一个收集器
        // 这里是 获取到 对象的名称，然后把对象的名称比较字符串长度 谁长显示谁 如果有 桂林林 则 广西 就显示 桂林林
        Map<String, Optional<String>> collectMapping = readCities().collect(groupingBy(City::getState, mapping(City::getName, maxBy(Comparator.comparing(String::length)))));
        System.out.println("collectMapping -> " + collectMapping);

        //这里还可以 把某值直接整合成一个集合
        Map<String, Set<String>> collectMappingSet = readCities().collect(groupingBy(City::getState, mapping(City::getName, toSet())));
        System.out.println("collectMappingSet -> " + collectMappingSet);

        // 还可以 整合 某个 数值 得到 整理好的数值集合
        Map<String, IntSummaryStatistics> collectSummarizingInt = readCities().collect(groupingBy(City::getState, summarizingInt(City::getPopulation)));
        System.out.println("collectSummarizingInt -> " + collectSummarizingInt);

    }


    /**
     * 程序清单 -系统内置集合
     */
    static void testLocal() {
        Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
        Map<String, Set<Locale>> countryMap =
                locales.collect(groupingBy(Locale::getCountry, toSet()));
        System.out.println("countryMap -> " + countryMap);
    }

    public static Stream<City> readCities() {
        return Stream.of(
                new City("柳州", "广西", 1),
                new City("南宁", "广西", 2),
                new City("北京", "首都", 3),
                new City("上海", "直辖市", 4),
                new City("桂林", "广西", 5),
                //new City("桂林林", "广西", 12),
                new City("广州", "广东", 6),
                new City("来宾", "广西", 7),
                new City("贵港", "广西", 8),
                new City("百色", "广西", 9),
                new City("崇左", "广西", 10),
                new City("中山", "广东", 11)
        );
    }

    public static class City {
        String name;
        String state;
        Integer population;

        @Override
        public String toString() {
            return "City{" +
                    "name='" + name + '\'' +
                    ", state='" + state + '\'' +
                    ", population=" + population +
                    '}';
        }

        public City(String name, String state, int population) {
            this.name = name;
            this.state = state;
            this.population = population;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getState() {
            return state;
        }

        public void setState(String state) {
            this.state = state;
        }

        public int getPopulation() {
            return population;
        }

        public void setPopulation(int population) {
            this.population = population;
        }
    }

}
